unit QBPgWindow;
{$I QBuilderVerCtrl.inc}
interface

uses
  {$IFDEF VCL16}
    System.Classes,
    Vcl.Controls,
    Vcl.StdCtrls,
    Vcl.ComCtrls,
  {$ELSE}
    Classes,
    Controls,
    StdCtrls,
    ComCtrls,
  {$ENDIF}
  QBWindow,
  QBParser,
  QBStringTypes;

const
  PgSQLKeywords = {'ABORT,ACCESS,ADD,AFTER,AGGREGATE,ALIGNMENT,ALL,ALTER,ANALYZE,AND,ANY,AS,ASC,' +
    'BACKWARD,BASETYPE,BEFORE,BEGIN,BETWEEN,BINARY,BY,CACHE,CASCADE,CASE,CAST,' +
    'CHARACTERISTICS,CHECK,CHECKPOINT,CLOSE,CLUSTER,COLLATE,COLUMN,COMMENT,COMMIT,' +
    'COMMUTATOR,CONSTRAINT,CONSTRAINTS,COPY,CREATE,CREATEDB,CREATEUSER,CROSS,' +
    'CURRENT_TIMESTAMP,CURSOR,CYCLE,DATABASE,DECLARE,DEFAULT,DEFERRABLE,' +
    'DELETE,DELIMITER,DELIMITERS,DESC,DISTINCT,DO,DROP,EACH,ELEMENT,ELSE,ENCODING,' +
    'END,ESCAPE,EXCEPT,EXCLUSIVE,EXECUTE,EXISTS,EXPLAIN,EXTERNALLENGTH,FALSE,FETCH,' +
    'FOR,FORCE,FOREIGN,FORWARD,FREEZE,FROM,FULL,FUNCTION,GRANT,GROUP,HANDLER,HASHES,' +
    'HAVING,ILIKE,IMMEDIATE,IN,INCREMENT,INDEX,INHERITS,INITCOND,INITIALLY,INNER,INPUT,INSENSITIVE,' +
    'INSERT,INSTEAD,INTERNALLENGTH,INTERSECT,INTO,IS,ISOLATION,JOIN,KEY,LANCOMPILER,LANGUAGE,' +
    'LEFT,LEFTARG,LEVEL,LIKE,LIMIT,LISTEN,LOAD,LOCAL,LOCATION,LOCK,MATCH,MAXVALUE,MINVALUE,' +
    'MOVE,NATURAL,NEGATOR,NEW,NEXT,NOCREATEDB,NOCREATEUSER,NONE,NOT,NOTHING,NOTIFY,NULL,' +
    'OF,OFF,OFFSET,OIDS,OLD,ON,ONLY,OPERATOR,OPTION,OR,ORDER,OUTER,OUTPUT,OWNER,' +
    'PASSEDBYVALUE,PASSWORD,PRIMARY,PRIOR,PRIVILEGES,PROCEDURAL,PROCEDURE,' +
    'RECEIVE,REFERENCES,REINDEX,RELATIVE,RENAME,REPLACE,RESET,RESTRICT,RETURNS,' +
    'RIGHT,RIGHTARG,ROLLBACK,ROW,RULE,SCROLL,SELECT,SEND,SEQUENCE,SERIALIZABLE,' +
    'SESSION_USER,SET,SFUNC,SHARE,SHOW,SOME,SORT1,SORT2,START,STATEMENT,STATISTICS,' +
    'STDOUT,STORAGE,STYPE,SYSID,TABLE,TEMP,TEMPLATE,TEMPORARY,THEN,TO,TOAST,' +
    'TRIGGER,TRUE,TRUNCATE,TRUSTED,TYPE,UNENCRYPTED,UNION,UNIQUE,UNKNOWN,UNLISTEN,' +
    'UPDATE,USER,USING,VACUUM,VALID,VALUES,VARIABLE,VERBOSE,VIEW,WHERE,WITH,WITHOUT,' +
    'ZONE,_ABSTIME,_ACLITEM,_BIT,_BOOL,_BOX,_BPCHAR,_BYTEA,_CHAR,_CID,_CIDR,_CIRCLE,' +
    '_FLOAT4,_FLOAT8,_INET,_INT2,_INT2VECTOR,_INT4,_INT8,_INTERVAL,_LINE,_LSEG,' +
    '_MONEY,_NAME,_NUMERIC,_OID,_OIDVECTOR,_PATH,_POINT,_POLYGON,_REGPROC,_RELTIME,' +
    '_TID,_TIME,_TIMESTAMP,_TIMESTAMPTZ,_TIMETZ,_TINTERVAL,_VARBIT,_VARCHAR,_XID,' +
    'ACLITEM,BIGINT,BIGSERIAL,BIT,BOOL,BOOLEAN,BOX,BPCHAR,BYTEA,CHAR,CID,CIDR,' +
    'DATE,DOUBLE,FLOAT4,FLOAT8,INET,INT2,INT2VECTOR,INT4,INT8,INTEGER,INTERVAL,' +
    'LSEG,MACADDR,MONEY,NUMERIC,OID,OIDVECTOR,OPAQUE,PATH,POINT,POLYGON,PRECISION,' +
    'REFCURSOR,REGPROC,RELTIME,SERIAL,SMALLINT,SMGR,TEXT,TID,TIME,TIMESTAMP,' +
    'TIMETZ,TINTERVAL,VARBIT,VARCHAR,VARYING,WHEN,XID,' +
    'HOUR,DAY,DECADE,CENTURY,DOW,DOY,EPOCH,MICROSECONDS,MILLENNIUM,' +
    'MILLISECONDS,QUARTER,SECOND,WEEK,YEAR'; }
    // ayz: only reserved keywords are needed
    'ALL,ANALYSE,ANALYZE,AND,ANY,ARRAY,AS,ASC,ASYMMETRIC,AUTHORIZATION,BETWEEN,BINARY,' +
    'BOTH,BY,CASE,CAST,CHECK,COLLATE,COLUMN,CONSTRAINT,CREATE,CROSS,CURRENT_DATE,' +
    'CURRENT_ROLE,CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_USER,DEFAULT,DEFERRABLE,DESC,' +
    'DISTINCT,DO,ELSE,END,EXCEPT,FALSE,FOR,FOREIGN,FREEZE,FROM,FULL,GRANT,GROUP,HAVING,' +
    'ILIKE,IN,INITIALLY,INNER,INTERSECT,INTO,IS,ISNULL,JOIN,LEADING,LEFT,LIKE,LIMIT,' +
    'LOCALTIME,LOCALTIMESTAMP,NATURAL,NEW,NOT,NOTNULL,NULL,OFF,OFFSET,OLD,ON,ONLY,OR,' +
    'ORDER,OUTER,OVERLAPS,PLACING,PRIMARY,REFERENCES,RETURNING,RIGHT,SELECT,SESSION_USER,' +
    'SIMILAR,SOME,SYMMETRIC,TABLE,THEN,TO,TRAILING,TRUE,UNION,UNIQUE,USER,USING,VERBOSE,' +
    'WHEN,WHERE';

  PgSQLFunctions: aqbString =
  'ABBREV,ABS,ABSTIME,ABSTIMEEQ,ABSTIMEGE,ABSTIMEGT,CURRENT_DATE,' +
    'ABSTIMELT,ABSTIMENE,ACLCONTAINS,ACLINSERT,ACLITEMIN,ACLITEMOUT,ACLREMOVE,' +
    'AGE,AREA,AREAJOINSEL,AREASEL,ARRAY_DIMS,ARRAY_EQ,ARRAY_IN,ARRAY_OUT,ASCII,' +
    'ATAN,ATAN2,AVG,BIT_IN,BIT_LENGTH,BIT_OUT,BITAND,BITCAT,BITCMP,BITEQ,BITFROMINT4,' +
    'BITGT,BITLE,BITLT,BITNE,BITNOT,BITOR,BITSHIFTLEFT,BITSHIFTRIGHT,BITTOINT4,' +
    'BOOLEQ,BOOLGE,BOOLGT,BOOLIN,BOOLLE,BOOLLT,BOOLNE,BOOLOUT,BOX_ABOVE,BOX_ADD,' +
    'BOX_CENTER,BOX_CONTAIN,BOX_CONTAINED,BOX_DISTANCE,BOX_DIV,BOX_EQ,BOX_GE,BOX_GT,' +
    'BOX_INTERSECT,BOX_LE,BOX_LEFT,BOX_LT,BOX_MUL,BOX_OUT,BOX_OVERLAP,BOX_OVERLEFT,' +
    'BOX_RIGHT,BOX_SAME,BOX_SUB,BPCHARCMP,BPCHAREQ,BPCHARGE,BPCHARGT,BPCHARIN,' +
    'BPCHARLT,BPCHARNE,BPCHAROUT,BROADCAST,BTABSTIMECMP,BTBEGINSCAN,BTBOOLCMP,' +
    'BTBULKDELETE,BTCHARCMP,BTCOSTESTIMATE,BTENDSCAN,BTFLOAT4CMP,BTFLOAT8CMP,BTGETTUPLE,' +
    'BTINT2CMP,BTINT4CMP,BTINT8CMP,BTMARKPOS,BTNAMECMP,BTOIDCMP,BTOIDVECTORCMP,' +
    'BTRESTRPOS,BTRIM,BTTEXTCMP,BYTEACAT,BYTEACMP,BYTEAEQ,BYTEAGE,BYTEAGT,BYTEAIN,' +
    'BYTEALIKE,BYTEALT,BYTEANE,BYTEANLIKE,BYTEAOUT,CASE,CASH_DIV_FLT4,CASH_DIV_FLT8,' +
    'CASH_DIV_INT4,CASH_EQ,CASH_GE,CASH_GT,CASH_IN,CASH_LE,CASH_LT,CASH_MI,CASH_MUL_FLT4,' +
    'CASH_MUL_INT2,CASH_MUL_INT4,CASH_NE,CASH_OUT,CASH_PL,CASH_WORDS,CASHLARGER,' +
    'CBRT,CEIL,CENTER,CHAR_LENGTH,CHARDIV,CHAREQ,CHARGE,CHARGT,CHARIN,CHARLE,' +
    'CHARMI,CHARMUL,CHARNE,CHAROUT,CHARPL,CHR,CIDEQ,CIDIN,CIDOUT,CIDR_IN,' +
    'CIRCLE,CIRCLE_ABOVE,CIRCLE_ADD_PT,CIRCLE_BELOW,CIRCLE_CENTER,CIRCLE_CONTAIN,' +
    'CIRCLE_CONTAINED,CIRCLE_DISTANCE,CIRCLE_DIV_PT,CIRCLE_EQ,CIRCLE_GE,CIRCLE_GT,' +
    'CIRCLE_LE,CIRCLE_LEFT,CIRCLE_LT,CIRCLE_MUL_PT,CIRCLE_NE,CIRCLE_OUT,CIRCLE_OVERLAP,' +
    'CIRCLE_OVERRIGHT,CIRCLE_RIGHT,CIRCLE_SAME,CIRCLE_SUB_PT,CLOSE_LB,CLOSE_LS,' +
    'CLOSE_PB,CLOSE_PL,CLOSE_PS,CLOSE_SB,CLOSE_SL,COL_DESCRIPTION,CONTJOINSEL,' +
    'CONVERT,COS,COT,COUNT,CURRENT_USER,CURRTID,CURRTID2,CURRVAL,DATE_CMP,DATE_EQ,' +
    'DATE_GT,DATE_IN,DATE_LARGER,DATE_LE,DATE_LT,DATE_MI,DATE_MII,DATE_NE,DATE_OUT,' +
    'DATE_PLI,DATE_SMALLER,DATE_TRUNC,DATETIME_PL,DATETIMETZ_PL,DCBRT,DECODE,DEGREES,' +
    'DIAGONAL,DIAMETER,DIST_CPOLY,DIST_LB,DIST_PB,DIST_PC,DIST_PL,DIST_PPATH,DIST_PS,' +
    'DIST_SL,DLOG1,DLOG10,DPOW,DROUND,DSQRT,DTRUNC,ENCODE,EQJOINSEL,EQSEL,EXP,' +
    'FLOAT4_ACCUM,FLOAT48DIV,FLOAT48EQ,FLOAT48GE,FLOAT48GT,FLOAT48LE,FLOAT48LT,' +
    'FLOAT48MUL,FLOAT48NE,FLOAT48PL,FLOAT4ABS,FLOAT4DIV,FLOAT4EQ,FLOAT4GE,FLOAT4GT,' +
    'FLOAT4LARGER,FLOAT4LE,FLOAT4LT,FLOAT4MI,FLOAT4MUL,FLOAT4NE,FLOAT4OUT,FLOAT4PL,' +
    'FLOAT4UM,FLOAT4UP,FLOAT8_ACCUM,FLOAT8_AVG,FLOAT8_STDDEV,FLOAT8_VARIANCE,' +
    'FLOAT84EQ,FLOAT84GE,FLOAT84GT,FLOAT84LE,FLOAT84LT,FLOAT84MI,FLOAT84MUL,FLOAT84NE,' +
    'FLOAT8ABS,FLOAT8DIV,FLOAT8EQ,FLOAT8GE,FLOAT8GT,FLOAT8IN,FLOAT8LARGER,FLOAT8LE,' +
    'FLOAT8MI,FLOAT8MUL,FLOAT8NE,FLOAT8OUT,FLOAT8PL,FLOAT8SMALLER,FLOAT8UM,FLOAT8UP,' +
    'FLT4_MUL_CASH,FLT8_MUL_CASH,FORMAT_TYPE,GET_BIT,GET_BYTE,GETDATABASEENCODING,' +
    'GISTBEGINSCAN,GISTBUILD,GISTBULKDELETE,GISTCOSTESTIMATE,GISTENDSCAN,GISTGETTUPLE,' +
    'GISTMARKPOS,GISTRESCAN,GISTRESTRPOS,HAS_TABLE_PRIVILEGE,HASHBEGINSCAN,HASHBPCHAR,' +
    'HASHBULKDELETE,HASHCHAR,HASHCOSTESTIMATE,HASHENDSCAN,HASHFLOAT4,HASHFLOAT8,' +
    'HASHINSERT,HASHINT2,HASHINT4,HASHINT8,HASHMACADDR,HASHMARKPOS,HASHNAME,HASHOID,' +
    'HASHRESCAN,HASHRESTRPOS,HASHVARLENA,HEIGHT,HOST,ICLIKEJOINSEL,ICLIKESEL,ICNLIKEJOINSEL,' +
    'ICREGEXEQJOINSEL,ICREGEXEQSEL,ICREGEXNEJOINSEL,ICREGEXNESEL,INET_IN,' +
    'INITCAP,INT2_ACCUM,INT2_AVG_ACCUM,INT2_MUL_CASH,INT2_SUM,INT24DIV,INT24EQ,' +
    'INT24GT,INT24LE,INT24LT,INT24MI,INT24MOD,INT24MUL,INT24NE,INT24PL,INT28EQ,' +
    'INT28GT,INT28LE,INT28LT,INT28NE,INT2ABS,INT2AND,INT2DIV,INT2EQ,INT2FAC,INT2GE,' +
    'INT2IN,INT2LARGER,INT2LE,INT2LT,INT2MI,INT2MOD,INT2MUL,INT2NE,INT2NOT,INT2OR,' +
    'INT2PL,INT2SHL,INT2SHR,INT2SMALLER,INT2UM,INT2UP,INT2VECTOREQ,INT2VECTORIN,' +
    'INT2XOR,INT4_ACCUM,INT4_AVG_ACCUM,INT4_MUL_CASH,INT4_SUM,INT42DIV,INT42EQ,' +
    'INT42GT,INT42LE,INT42LT,INT42MI,INT42MOD,INT42MUL,INT42NE,INT42PL,INT44IN,' +
    'INT48DIV,INT48EQ,INT48GE,INT48GT,INT48LE,INT48LT,INT48MI,INT48MUL,INT48NE,' +
    'INT4ABS,INT4AND,INT4DIV,INT4EQ,INT4FAC,INT4GE,INT4GT,INT4IN,INT4INC,INT4LARGER,' +
    'INT4LT,INT4MI,INT4MOD,INT4MUL,INT4NE,INT4NOT,INT4NOTIN,INT4OR,INT4OUT,INT4PL,' +
    'INT4SHR,INT4SMALLER,INT4UM,INT4UP,INT4XOR,INT8_ACCUM,INT8_AVG,INT8_SUM,' +
    'INT82GE,INT82GT,INT82LE,INT82LT,INT82NE,INT84DIV,INT84EQ,INT84GE,INT84GT,' +
    'INT84LT,INT84MI,INT84MUL,INT84NE,INT84PL,INT8ABS,INT8AND,INT8DIV,INT8EQ,INT8FAC,' +
    'INT8GT,INT8IN,INT8INC,INT8LARGER,INT8LE,INT8LT,INT8MI,INT8MOD,INT8MUL,INT8NE,' +
    'INT8OR,INT8OUT,INT8PL,INT8SHL,INT8SHR,INT8SMALLER,INT8UM,INT8UP,INT8XOR,INTER_LB,' +
    'INTER_SL,INTERVAL_ACCUM,INTERVAL_AVG,INTERVAL_CMP,INTERVAL_DIV,INTERVAL_EQ,' +
    'INTERVAL_GT,INTERVAL_HASH,INTERVAL_IN,INTERVAL_LARGER,INTERVAL_LE,INTERVAL_LT,' +
    'INTERVAL_MI_TIME,INTERVAL_MI_TIMETZ,INTERVAL_MUL,INTERVAL_NE,INTERVAL_OUT,' +
    'INTERVAL_PL_TIME,INTERVAL_SMALLER,INTERVAL_UM,INTINTERVAL,ISCLOSED,ISFALSE,' +
    'ISHORIZONTAL,ISNOTFALSE,ISNOTTRUE,ISOPEN,ISPARALLEL,ISPERP,ISTRUE,ISVERTICAL,' +
    'LIKE_ESCAPE,LIKEJOINSEL,LIKESEL,LINE,LINE_DISTANCE,LINE_EQ,LINE_HORIZONTAL,' +
    'LINE_INTERPT,LINE_INTERSECT,LINE_OUT,LINE_PARALLEL,LINE_PERP,LINE_VERTICAL,' +
    'LO_CLOSE,LO_CREAT,LO_EXPORT,LO_IMPORT,LO_LSEEK,LO_OPEN,LO_TELL,LO_UNLINK,' +
    'LOREAD,LOWER,LOWRITE,LPAD,LSEG_CENTER,LSEG_DISTANCE,LSEG_EQ,LSEG_GE,' +
    'LSEG_HORIZONTAL,LSEG_IN,LSEG_INTERPT,LSEG_INTERSECT,LSEG_LE,LSEG_LENGTH,LSEG_LT,' +
    'LSEG_OUT,LSEG_PARALLEL,LSEG_PERP,LSEG_VERTICAL,LTRIM,MACADDR_CMP,' +
    'MACADDR_GE,MACADDR_GT,MACADDR_IN,MACADDR_LE,MACADDR_LT,MACADDR_NE,MACADDR_OUT,MAX,' +
    'MIN,MKTINTERVAL,MOD,MUL_D_INTERVAL,NABSTIMEIN,NABSTIMEOUT,NAMEEQ,NAMEGE,' +
    'NAMEICLIKE,NAMEICNLIKE,NAMEICREGEXEQ,NAMEICREGEXNE,NAMEIN,NAMELE,NAMELIKE,' +
    'NAMENE,NAMENLIKE,NAMEOUT,NAMEREGEXEQ,NAMEREGEXNE,NEQJOINSEL,NEQSEL,NETMASK,' +
    'NETWORK_CMP,NETWORK_EQ,NETWORK_GE,NETWORK_GT,NETWORK_LE,NETWORK_LT,NETWORK_NE,' +
    'NETWORK_SUBEQ,NETWORK_SUP,NETWORK_SUPEQ,NEXTVAL,NLIKEJOINSEL,NLIKESEL,NONNULLVALUE,' +
    'NOW,NPOINTS,NULLVALUE,NUMERIC_ABS,NUMERIC_ACCUM,NUMERIC_ADD,NUMERIC_AVG,' +
    'NUMERIC_DIV,NUMERIC_EQ,NUMERIC_EXP,NUMERIC_GE,NUMERIC_GT,NUMERIC_IN,NUMERIC_INC,' +
    'NUMERIC_LE,NUMERIC_LN,NUMERIC_LOG,NUMERIC_LT,NUMERIC_MOD,NUMERIC_MUL,NUMERIC_NE,' +
    'NUMERIC_POWER,NUMERIC_SMALLER,NUMERIC_SQRT,NUMERIC_STDDEV,NUMERIC_SUB,NUMERIC_UMINUS,' +
    'NUMERIC_VARIANCE,OBJ_DESCRIPTION,OCTET_LENGTH,OIDEQ,OIDGE,OIDGT,OIDIN,' +
    'OIDLE,OIDLT,OIDNE,OIDNOTIN,OIDOUT,OIDRAND,OIDSMALLER,OIDSRAND,OIDVECTOREQ,' +
    'OIDVECTORGT,OIDVECTORIN,OIDVECTORLE,OIDVECTORLT,OIDVECTORNE,OIDVECTOROUT,' +
    'ON_PB,ON_PL,ON_PPATH,ON_PS,ON_SB,ON_SL,OVERLAPS,PATH_ADD,PATH_ADD_PT,' +
    'PATH_CONTAIN_PT,PATH_DISTANCE,PATH_DIV_PT,PATH_IN,PATH_INTER,PATH_LENGTH,' +
    'PATH_N_EQ,PATH_N_GE,PATH_N_GT,PATH_N_LE,PATH_N_LT,PATH_NPOINTS,PATH_OUT,PATH_SUB_PT,' +
    'PG_CHAR_TO_ENCODING,PG_CLIENT_ENCODING,PG_ENCODING_TO_CHAR,PG_GET_EXPR,PG_GET_INDEXDEF,' +
    'PG_GET_USERBYID,PG_GET_VIEWDEF,PG_STAT_GET_BACKEND_ACTIVITY,PG_STAT_GET_BACKEND_DBID,' +
    'PG_STAT_GET_BACKEND_PID,PG_STAT_GET_BACKEND_USERID,PG_STAT_GET_BLOCKS_FETCHED,' +
    'PG_STAT_GET_DB_BLOCKS_FETCHED,PG_STAT_GET_DB_BLOCKS_HIT,PG_STAT_GET_DB_NUMBACKENDS,' +
    'PG_STAT_GET_DB_XACT_ROLLBACK,PG_STAT_GET_NUMSCANS,PG_STAT_GET_TUPLES_DELETED,' +
    'PG_STAT_GET_TUPLES_INSERTED,PG_STAT_GET_TUPLES_RETURNED,PG_STAT_GET_TUPLES_UPDATED,' +
    'POINT_ABOVE,POINT_ADD,POINT_BELOW,POINT_DISTANCE,POINT_DIV,POINT_EQ,' +
    'POINT_IN,POINT_LEFT,POINT_MUL,POINT_NE,POINT_OUT,POINT_RIGHT,POINT_SUB,POINT_VERT,' +
    'POLY_CONTAIN,POLY_CONTAIN_PT,POLY_CONTAINED,POLY_DISTANCE,POLY_IN,POLY_LEFT,' +
    'POLY_OUT,POLY_OVERLAP,POLY_OVERLEFT,POLY_OVERRIGHT,POLY_RIGHT,POLY_SAME,' +
    'POSITION,POSITIONJOINSEL,POSITIONSEL,POW,PT_CONTAINED_CIRCLE,PT_CONTAINED_POLY,' +
    'QUOTE_LITERAL,RADIANS,RADIUS,RANDOM,REGEXEQJOINSEL,REGEXEQSEL,REGEXNEJOINSEL,' +
    'REGPROCIN,REGPROCOUT,REGPROCTOOID,RELTIMEEQ,RELTIMEGE,RELTIMEGT,RELTIMEIN,' +
    'RELTIMELT,RELTIMENE,RELTIMEOUT,REPEAT,RI_FKEY_CASCADE_DEL,RI_FKEY_CASCADE_UPD,' +
    'RI_FKEY_CHECK_UPD,RI_FKEY_NOACTION_DEL,RI_FKEY_NOACTION_UPD,RI_FKEY_RESTRICT_DEL,' +
    'RI_FKEY_SETDEFAULT_DEL,RI_FKEY_SETDEFAULT_UPD,RI_FKEY_SETNULL_DEL,RI_FKEY_SETNULL_UPD,' +
    'RPAD,RT_BIGBOX_SIZE,RT_BOX_INTER,RT_BOX_SIZE,RT_BOX_UNION,RT_POLY_INTER,RT_POLY_SIZE,' +
    'RTBEGINSCAN,RTBUILD,RTBULKDELETE,RTCOSTESTIMATE,RTENDSCAN,RTGETTUPLE,RTINSERT,' +
    'RTRESCAN,RTRESTRPOS,RTRIM,SCALARGTJOINSEL,SCALARGTSEL,SCALARLTJOINSEL,SCALARLTSEL,' +
    'SET_BIT,SET_BYTE,SET_MASKLEN,SETEVAL,SETSEED,SETVAL,SIGN,SIN,SLOPE,SMGREQ,' +
    'SMGRNE,SMGROUT,SQRT,STDDEV,STRPOS,SUBSTR,SUBSTRING,SUM,TAN,TEXT_GE,TEXT_GT,TEXT_LARGER,' +
    'TEXT_LT,TEXT_SMALLER,TEXTCAT,TEXTEQ,TEXTICLIKE,TEXTICNLIKE,TEXTICREGEXEQ,' +
    'TEXTIN,TEXTLEN,TEXTLIKE,TEXTNE,TEXTNLIKE,TEXTOUT,TEXTREGEXEQ,TEXTREGEXNE,' +
    'TIDIN,TIDOUT,TIME_CMP,TIME_EQ,TIME_GE,TIME_GT,TIME_IN,TIME_LARGER,TIME_LE,' +
    'TIME_MI_INTERVAL,TIME_MI_TIME,TIME_NE,TIME_OUT,TIME_PL_INTERVAL,TIME_SMALLER,' +
    'TIMEMI,TIMENOW,TIMEOFDAY,TIMEPL,TIMESTAMP_CMP,TIMESTAMP_EQ,TIMESTAMP_GE,' +
    'TIMESTAMP_IN,TIMESTAMP_LARGER,TIMESTAMP_LE,TIMESTAMP_LT,TIMESTAMP_MI,TIMESTAMP_MI_SPAN,' +
    'TIMESTAMP_OUT,TIMESTAMP_PL_SPAN,TIMESTAMP_SMALLER,TIMESTAMPTZ_CMP,' +
    'TIMESTAMPTZ_GE,TIMESTAMPTZ_GT,TIMESTAMPTZ_IN,TIMESTAMPTZ_LARGER,TIMESTAMPTZ_LE,' +
    'TIMESTAMPTZ_MI,TIMESTAMPTZ_MI_SPAN,TIMESTAMPTZ_NE,TIMESTAMPTZ_OUT,TIMESTAMPTZ_PL_SPAN,' +
    'TIMETZ_CMP,TIMETZ_EQ,TIMETZ_GE,TIMETZ_GT,TIMETZ_HASH,TIMETZ_IN,TIMETZ_LARGER,' +
    'TIMETZ_LT,TIMETZ_MI_INTERVAL,TIMETZ_NE,TIMETZ_OUT,TIMETZ_PL_INTERVAL,TIMETZ_SMALLER,' +
    'TIMEZONE,TINTERVALCT,TINTERVALEND,TINTERVALEQ,TINTERVALGE,TINTERVALGT,' +
    'TINTERVALLE,TINTERVALLENEQ,TINTERVALLENGE,TINTERVALLENGT,TINTERVALLENLE,TINTERVALLENLT,' +
    'TINTERVALLT,TINTERVALNE,TINTERVALOUT,TINTERVALOV,TINTERVALREL,TINTERVALSAME,' +
    'TO_ASCII,TO_CHAR,TO_DATE,TO_NUMBER,TO_TIMESTAMP,TRANSLATE,TRUNC,UPDATE_PG_PWD,' +
    'USERFNTEST,VARBIT_IN,VARBIT_OUT,VARBITCMP,VARBITEQ,VARBITGE,VARBITGT,' +
    'VARBITLT,VARBITNE,VARCHARCMP,VARCHAREQ,VARCHARGE,VARCHARGT,VARCHARIN,' +
    'VARCHARLT,VARCHARNE,VARCHAROUT,VARIANCE,VERSION,WIDTH,XIDEQ,XIDIN,XIDOUT,' +
    'EXTRACT';

  PgSQLOperations = '+,-,*,/,||,::';
  PgSQLCompareOperators = '!=,=,<>,<,<=,>,>=,~,~*,!~,!~*';

  PgSQLBoolFalse = 'False';
  PgSQLBoolTrue = 'True';

type

  TQBPgSQLOptions = class(TPersistent)
  private
    FLimitRows: integer;
    FOffset: integer;
    FParser: TQBSelectParser;
    FDistinctFields: TaqbStrings;
    function GetLimitScript: aqbString;
    function GetOffsetScript: aqbString;
    function GetDistinctFieldsScript: aqbString;
    procedure SetDistinctFields(const Value: TaqbStrings);
  public
    constructor Create(AParser: TQBSelectParser);
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    procedure Clear;

    property Offset: integer read FOffset write FOffset;
    property LimitRows: integer read FLimitRows write FLimitRows;

    property DistinctFields: TaqbStrings read FDistinctFields write SetDistinctFields;
    property LimitScript: aqbString read GetLimitScript;
    property DistinctFieldsScript: aqbString read GetDistinctFieldsScript;
    property OffsetScript: aqbString read GetOffsetScript;
    property Parser: TQBSelectParser read FParser;
  end;

  TQBPgSQLScanner = class(TQBSQLScanner)
  private
    FUseNamespaces: boolean;
  protected
    function GetIdentQuote: aqbChar; override;
    function GetProcessAliases: boolean; override;
//    function IsAlpha(Ch: aqbChar): boolean; override;
    function IsQuote(Ch: aqbChar): boolean; override;
    function IsIdentifier(const S: aqbString): boolean; override;
    function GetCommaKeywords: aqbString; override;
    function GetCommaFunctions: aqbString; override;
    function GetCommaOperations: aqbString; override;
    function GetCommaCompareOperators: aqbString; override;
    function GetQuoteIdentifiers: boolean; override;
    function ProcessIdent(const S: aqbString): aqbString; override;
    procedure DefineBooleans; override;
    function GetFormatTokenIdent(const Ident: aqbString;
      const Quoted: boolean): aqbString; override;
  public
    constructor Create; override;
    function ProcessName(const S: aqbString): aqbString; override;
    function IsIdentQuote(Ch: aqbChar): boolean; override;
    function NeedQuotes(const Ident: aqbString): boolean; override;
    property UseNamespaces: boolean read FUseNamespaces write FUseNamespaces
      default false;
  end;

  TQBPgSelectParser = class(TQBSelectParser)
  private
    FPgSQLOptions: TQBPgSQLOptions;
    procedure SetPgSQLOptions(const Value: TQBPgSQLOptions);
  protected
    function CreateScanner: TQBSQLScanner; override;
    function GetExpression: aqbString; override;
    procedure DefineOperators; override;
    procedure DoParseSelectClause; override;
    procedure DoParseDistinct; virtual;
    procedure DoParseAppendix; override;
    function GetCaseSensitiveIdentifiers: boolean; override;
    function IsSchemaSupported: boolean; override;
    function NeedParentheses: boolean; override;
    function IsConstInJoin: boolean; override;
    function IsSpaceDelimNeed(const PrevTokenStr, CurrTokenStr: aqbString;
      const PrevTokenKind, CurrTokenKind: TTokenKinds): boolean; override;
  public
    constructor Create(AParent: TPersistent); override;
    destructor Destroy; override;
    procedure Clear; override;
    function IsLowerCaseAliases: boolean; override;
    property PgSQLOptions: TQBPgSQLOptions read FPgSQLOptions write
      SetPgSQLOptions;
  end;

  TPgSQLQueryBuilder = class(TFullQueryBuilder {TCustomQueryBuilder})
  private
    function GetUseNamespaces: boolean;
    procedure SetUseNamespaces(const Value: boolean);
  public
    function CreateParser(AParent: TPersistent): TQBSelectParser; override;
  published
    property About;
    property Align;
    property Aggregates;
    property Functions;
    property Keywords;
    property Operators;
    property Options;
    property Palette;
    property ScriptFormat;
    property Storage;
    property Style;
    property TabHeight;
    property UseNamespaces: boolean read GetUseNamespaces write SetUseNamespaces
      default False;
    property VisibleTabs;
    property _Version;
    // ayz
    property WorkAreaPopupMenu;
    property TabsPopupMenu;
    // \ayz

    property OnStartDrag;
    property OnDragOver;
    property OnDragDrop;
    property OnEndDrag;
    {$IFNDEF EC_UNICODE}
    property OnGetTableFields;
    property OnParserError;
    {$ELSE}
    property OnGetTableFieldsW;
    property OnParserErrorW;
    {$ENDIF}
    property OnAddRemoveTable;
    property OnAddRemoveLink;
    property OnEnterProcParameter;
  end;

implementation

uses
  {$IFDEF VCL16}
    System.SysUtils,
  {$ELSE}
    SysUtils,
  {$ENDIF}
  QBUtils;

const
  sSelect = 'SELECT';
  sLimit = 'LIMIT';
  sOffset = 'OFFSET';
  sDistinct = 'DISTINCT';
  sOn = 'ON';

  { TQBPgSQLScanner }

constructor TQBPgSQLScanner.Create;
begin
  inherited;
  EnableNameSpaces := true;
end;

procedure TQBPgSQLScanner.DefineBooleans;
begin
  Booleans[False] := PgSQLBoolFalse;
  Booleans[True] := PgSQLBoolTrue;
end;

function TQBPgSQLScanner.GetCommaCompareOperators: aqbString;
begin
  Result := PgSQLCompareOperators;
end;

function TQBPgSQLScanner.GetCommaFunctions: aqbString;
begin
  Result := PgSQLFunctions;
end;

function TQBPgSQLScanner.GetCommaKeywords: aqbString;
begin
  Result := PgSQLKeywords;
end;

function TQBPgSQLScanner.GetCommaOperations: aqbString;
begin
  Result := PgSQLOperations;
end;

function TQBPgSQLScanner.GetFormatTokenIdent(const Ident: aqbString;
  const Quoted: boolean): aqbString;
begin
  Result := inherited GetFormatTokenIdent(Ident, Quoted);
  {if Pos('"', Ident) = 0 then
    Result := QuoteName(Ident);}
  // If identifier is not quoted - if is treated be server as in lower case
  if not Quoted then
    Result := AnsiLowerCase(Ident);
end;

function TQBPgSQLScanner.GetIdentQuote: aqbChar;
begin
  Result := '"';
end;

function TQBPgSQLScanner.GetProcessAliases: boolean;
begin
  Result := True;
end;

function TQBPgSQLScanner.GetQuoteIdentifiers: boolean;
begin
  Result := True;
end;
(*
function TQBPgSQLScanner.IsAlpha(Ch: aqbChar): boolean;
begin
  Result := Ch in ['a'..'z', 'A'..'Z']; // ab 25.03.2004
end;
*)
function TQBPgSQLScanner.IsIdentifier(const S: aqbString): boolean;
var
  I: integer;
begin
  Result := (S <> '') and (IsAlpha(S[1]) or (S[1] = '_'));
  if not Result then
    Exit;

  for I := 2 to Length(S) do
  begin
    Result := IsLegalSymbol(S[I]);
    if not Result then
      Break;
  end;
end;

function TQBPgSQLScanner.IsIdentQuote(Ch: aqbChar): boolean;
begin
  Result := Ch = '"';
end;

function TQBPgSQLScanner.IsQuote(Ch: aqbChar): boolean;
begin
  Result := Ch = '''';
end;

function TQBPgSQLScanner.NeedQuotes(const Ident: aqbString): boolean;
var
  HasUppercaseLetters: Boolean;
begin
  if (Length(Ident) > 1) and IsIdentQuote(Ident[1]) and
    IsIdentQuote(Ident[Length(Ident)]) then
    Result := false
  else
  begin
    Result := IsKeyword(Ident) or IsFunction(Ident);
    // ayznew
    //if not Result and (inherited NeedQuotes(Ident) or ) then
      //Result := not IsIdentifier(Ident);
    HasUppercaseLetters := QBLowerCase(Ident) <> Ident;
    if not Result and (inherited NeedQuotes(Ident) or HasUppercaseLetters) then
      Result := not IsIdentifier(Ident) or HasUppercaseLetters;
    // \ayznew
  end;
end;

function TQBPgSQLScanner.ProcessIdent(const S: aqbString): aqbString;
begin
  Result := inherited ProcessIdent(S);
end;

function TQBPgSQLScanner.ProcessName(const S: aqbString): aqbString;
begin
  Result := inherited ProcessName(S);
end;

{ TQBPgSQLOptions }

procedure TQBPgSQLOptions.Assign(Source: TPersistent);
begin
  if Source is TQBPgSQLOptions then
  begin
    FOffset := TQBPgSQLOptions(Source).Offset;
    FLimitRows := TQBPgSQLOptions(Source).LimitRows;
  end
  else
    inherited Assign(Source);
end;

procedure TQBPgSQLOptions.Clear;
begin
  FOffset := 0;
  FLimitRows := 0;
  FDistinctFields.Clear();
end;

constructor TQBPgSQLOptions.Create(AParser: TQBSelectParser);
begin
  inherited Create();
  FParser := AParser;
  FDistinctFields := TaqbStringList.Create();
end;

destructor TQBPgSQLOptions.Destroy;
begin
  FDistinctFields.Free();
  inherited Destroy();
end;

function TQBPgSQLOptions.GetDistinctFieldsScript: aqbString;
var
  I: integer;
  S: aqbString;
begin
  Result := '';
  if Assigned(FParser) and FParser.Distinct then
    Result := sDistinct + ' ';
  if FDistinctFields.Count > 0 then
    Result := Result + sOn + ' (%s) '
  else
    Exit;
  for I := 0 to FDistinctFields.Count - 1 do
  begin
    if Assigned(FParser) then
      S := S + FParser.Scanner.ProcessName(FDistinctFields[I])
    else
      S := S + FDistinctFields[I];
    if I < FDistinctFields.Count - 1 then
      S := S + ', ';
  end;
  Result := QBFormat(Result, [S]);
end;

function TQBPgSQLOptions.GetLimitScript: aqbString;
var
  S: aqbString;
begin
  Result := '';
  if FLimitRows > 0 then
  begin
    S := sLimit;
    if Assigned(FParser) then
      S := FParser.ProcessKeyword(S);
    Result := QBFormat(S + ' %d', [FLimitRows]);
  end;
  if Result <> '' then
    Result := Result + sEOL;
end;

function TQBPgSQLOptions.GetOffsetScript: aqbString;
var
  S: aqbString;
begin
  Result := '';
  if FOffset > 0 then
  begin
    S := sOffset;
    if Assigned(FParser) then
      S := FParser.ProcessKeyword(S);
    Result := QBFormat(S + ' %d', [FOffset]);
  end;
  if Result <> '' then
    Result := Result + sEOL;
end;

procedure TQBPgSQLOptions.SetDistinctFields(const Value: TaqbStrings);
begin
  if FDistinctFields <> Value then
    FDistinctFields.Assign(Value);
end;

{ TQBPgSelectParser }

procedure TQBPgSelectParser.Clear;
begin
  inherited Clear();
  FPgSQLOptions.Clear();
end;


constructor TQBPgSelectParser.Create(AParent: TPersistent);
begin
  inherited Create(AParent);
  FPgSQLOptions := TQBPgSQLOptions.Create(Self);
end;

function TQBPgSelectParser.CreateScanner: TQBSQLScanner;
begin
  Result := TQBPgSQLScanner.Create();
end;

function TQBPgSelectParser.IsSpaceDelimNeed(const PrevTokenStr, CurrTokenStr: aqbString;
  const PrevTokenKind, CurrTokenKind: TTokenKinds): boolean;
begin
  if ((CurrTokenStr = '::') and (tkIdent in PrevTokenKind)) or
     ((PrevTokenStr = '::') and IsIntersected([tkKeyword, tkFunction, tkIdent], CurrTokenKind)) then
    Result := False
  else
    Result := inherited IsSpaceDelimNeed(PrevTokenStr, CurrTokenStr, PrevTokenKind, CurrTokenKind);
end;

procedure TQBPgSelectParser.DefineOperators;
var
  Operator: TQBOperator;
begin
  Operators.Clear();

  Operator := Operators.Add();
  Operator.Name := '=';
  Operator.Caption := '=';

  Operator := Operators.Add();
  Operator.Name := '<>';
  Operator.Caption := '<>';

  Operator := Operators.Add();
  Operator.Name := '<';
  Operator.Caption := '<';

  Operator := Operators.Add();
  Operator.Name := '>';
  Operator.Caption := '>';

  Operator := Operators.Add();
  Operator.Name := '<=';
  Operator.Caption := '<=';

  Operator := Operators.Add();
  Operator.Name := '>=';
  Operator.Caption := '>=';

  Operator := Operators.Add();
  Operator.Name := '~';
  Operator.Caption := '~';

  Operator := Operators.Add();
  Operator.Name := '~*';
  Operator.Caption := '~*';

  Operator := Operators.Add();
  Operator.Name := '!~';
  Operator.Caption := '!~';

  Operator := Operators.Add();
  Operator.Name := '!~*';
  Operator.Caption := '!~*';

  Operator := Operators.Add();
  Operator.Name := 'LIKE';
  Operator.Caption := 'LIKE';

  Operator := Operators.Add();
  Operator.Name := 'NOT LIKE';
  Operator.Caption := 'NOT LIKE';

  Operator := Operators.Add();
  Operator.Name := 'ILIKE';
  Operator.Caption := 'ILIKE';

  Operator := Operators.Add();
  Operator.Name := 'NOT ILIKE';
  Operator.Caption := 'NOT ILIKE';

  Operator := Operators.Add();
  Operator.Name := 'IN';
  Operator.Caption := 'IN';
  Operator.Prefix := '(';
  Operator.Postfix := ')';
  Operator.CommaList := True;

  Operator := Operators.Add();
  Operator.Name := 'NOT IN';
  Operator.Caption := 'NOT IN';
  Operator.Prefix := '(';
  Operator.Postfix := ')';
  Operator.CommaList := True;

  Operator := Operators.Add();
  Operator.Name := 'BETWEEN';
  Operator.Caption := 'BETWEEN';
  Operator.Relate := 'AND';
  Operator.Count := 2;

  Operator := Operators.Add();
  Operator.Name := 'NOT BETWEEN';
  Operator.Caption := 'NOT BETWEEN';
  Operator.Relate := 'AND';
  Operator.Count := 2;

  Operator := Operators.Add();
  Operator.Name := 'IS NULL';
  Operator.Caption := 'IS NULL';
  Operator.Count := 0;

  Operator := Operators.Add();
  Operator.Name := 'IS NOT NULL';
  Operator.Caption := 'IS NOT NULL';
  Operator.Count := 0;

  Operator := Operators.Add();
  Operator.Name := '!=';
  Operator.Caption := '!=';

end;

destructor TQBPgSelectParser.Destroy;
begin
  FPgSQLOptions.Free();
  inherited Destroy();
end;

procedure TQBPgSelectParser.DoParseAppendix;
begin
  if (CompareText(Scanner.Token, sLimit) = 0) or
    (CompareText(Scanner.Token, sOffset) = 0) then
  begin
    if CompareText(Scanner.Token, sLimit) = 0 then
    begin
      Scanner.GetNextToken();
      if tkInteger in Scanner.TokenKind then
      begin
        FPgSQLOptions.LimitRows := StrToInt(Scanner.Token);
        Scanner.GetNextToken();
      end
      else
      begin
        State := ssError;
        Exit;
      end;
    end;
    if CompareText(Scanner.Token, sOffset) = 0 then
    begin
      Scanner.GetNextToken();
      if tkInteger in Scanner.TokenKind then
      begin
        FPgSQLOptions.Offset := StrToInt(Scanner.Token);
        Scanner.GetNextToken();
      end
      else
      begin
        State := ssError;
        Exit;
      end;
    end;
  end
  else
    inherited DoParseAppendix();
end;

procedure TQBPgSelectParser.DoParseDistinct;
var
  S: aqbString;
begin
  Scanner.GetNextToken();
  if Scanner.Token = '(' then
  begin
    Scanner.GetNextToken();
    S := '';
    while Scanner.Token <> ')' do
    begin
      if Scanner.Token = ',' then
      begin
        if S <> '' then
          FPgSQLOptions.DistinctFields.Add(S);
        S := '';
      end
      else if (tkIdent in Scanner.TokenKind) or (Scanner.Token = '.') then
      begin
        S := S + Scanner.Token
      end
      else
      begin
        State := ssError;
        Break;
      end;
      Scanner.GetNextToken();
    end;
    if S <> '' then
      FPgSQLOptions.DistinctFields.Add(S);
    Scanner.GetNextToken();
    State := ssFieldDefs;
  end
  else
    State := ssError;
end;

procedure TQBPgSelectParser.DoParseSelectClause;
begin
  inherited DoParseSelectClause();
  if State = ssError then
  begin
    if CompareText(Scanner.Token, sOn) = 0 then
      DoParseDistinct()
    else
      State := ssError;
  end;
end;

function TQBPgSelectParser.GetCaseSensitiveIdentifiers: boolean;
begin
  Result := true;
end;


function TQBPgSelectParser.GetExpression: aqbString;
var
  I: integer;
  AllNull: boolean;
begin
  {
  Result := TableDefs.Script;
  if Result <> '' then
    Result := ProcessKeyWord(sSelect) + ' ' + FPgSQLOptions.DistinctFieldsScript +
      Result + WhereItems.Script + GroupByItems.Script + HavingItems.Script +
      OrderByItems.Script + FPgSQLOptions.LimitScript +
      FPgSQLOptions.OffsetScript;
  }
  Result := TableDefs.Script;
  if Result <> '' then
    case TypeQuery of
      tqSelect: Result := ProcessKeyWord(sSelect) + ' ' + FPgSQLOptions.DistinctFieldsScript +
        Result + WhereItems.Script + GroupByItems.Script + HavingItems.Script +
          OrderByItems.Script + FPgSQLOptions.LimitScript +
          FPgSQLOptions.OffsetScript;
      tqInsert:
        begin
          AllNull := True;
          for I := 0 to TableDefs[0].FieldDefs.Count - 1 do
            if TableDefs[0].FieldDefs[I].Data <> '' then
              AllNull := False;
          if AllNull and (not ValuesItems.IsQuery) then
            Result := ProcessKeyWord('INSERT') + ' ' + ProcessKeyWord('INTO') +
              ' ' + Result {Scanner.ProcessName(TableDefs[0].Name)} + ' ' +
            ProcessKeyWord('DEFAULT') + ' ' + ProcessKeyWord('VALUES')
          else
            Result := ProcessKeyword('INSERT') + ' ' + ProcessKeyword('INTO') +
              Result + ValuesItems.Script;
        end;
      tqUpdate: Result := ProcessKeyWord('UPDATE') + ' ' + Result + WhereItems.Script;
      tqDelete: Result := ProcessKeyWord('DELETE') + ' ' + Result + WhereItems.Script;
    end;
end;

function TQBPgSelectParser.IsConstInJoin: boolean; 
begin
  Result := true;
end;

function TQBPgSelectParser.IsLowerCaseAliases: boolean;
begin
  Result := true;
end;

function TQBPgSelectParser.IsSchemaSupported: boolean;
begin
  Result := true;
end;

function TQBPgSelectParser.NeedParentheses: boolean;
begin
  Result := false;
end;

procedure TQBPgSelectParser.SetPgSQLOptions(const Value: TQBPgSQLOptions);
begin
  if FPgSQLOptions <> Value then
    FPgSQLOptions.Assign(Value);
end;

{ TPgSQLQueryBuilder }

function TPgSQLQueryBuilder.CreateParser(AParent: TPersistent): TQBSelectParser;
begin
  Result := TQBPgSelectParser.Create(AParent);
end;

function TPgSQLQueryBuilder.GetUseNamespaces: boolean;
begin
  Result := TQBPgSQLScanner(Parser.Scanner).UseNamespaces;
end;

procedure TPgSQLQueryBuilder.SetUseNamespaces(const Value: boolean);
begin
  TQBPgSQLScanner(Parser.Scanner).UseNamespaces := Value;
end;

end.

